package com.lms.service.impl;

import com.lms.bean.Book;
import com.lms.bean.Type;
import com.lms.dao.BookDao;
import com.lms.dao.TypeDao;
import com.lms.service.BookService;
import com.lms.util.MyBeanUtils;
import com.lms.util.MyStringUtils;
import com.lms.util.QueryResult;
import com.lms.vo.BookVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;

@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao bookDao;

    @Autowired
    private TypeDao typeDao;

    @Override
    public Long getTotalCount() {
        return bookDao.count();
    }

    @Override
    public List<BookVO> findAll(int page, int pageSize) {
        Sort sort = Sort.by(Sort.Direction.ASC, "bookId");
        Pageable pageable = PageRequest.of(page, pageSize, sort);
        List<Book> list = bookDao.getList(pageable);
        //将 Book 对象组装成 BookVO 对象
        List<BookVO> bookVOList = new ArrayList<>();
        list.forEach(book -> bookVOList.add(BookVO.convert(book)));
        return bookVOList;
    }

    @Override
    public Long getTotalCount(String bookName) {
        String s = StringUtils.trimAllWhitespace(bookName);
        return bookDao.countByBookName("%" + s + "%");
    }

    @Override
    public List<BookVO> findAll(int page, int pageSize, String bookName) {
        Sort sort = Sort.by(Sort.Direction.ASC, "bookId");
        Pageable pageable = PageRequest.of(page, pageSize, sort);
        String s = StringUtils.trimAllWhitespace(bookName);
        List<Book> list = bookDao.findByBookName("%" + s + "%", pageable);
        //将 Book 对象组装成 BookVO 对象
        List<BookVO> bookVOList = new ArrayList<>();
        list.forEach(book -> bookVOList.add(BookVO.convert(book)));
        return bookVOList;
    }

    @Override
    public Book findBookById(Long bookId) {
        return bookDao.findById(bookId).orElse(null);
    }

    @Override
    public void save(Book book) {
        Long bookId = book.getBookId();
        Type type = typeDao.getOne(book.getType().getTypeId());
        book.setType(type);
        if (null == bookId) {
            bookDao.save(book);
        } else {
            Book b = bookDao.getOne(bookId);
            BeanUtils.copyProperties(book, b, MyBeanUtils.getNullPropertyNames(book));
            bookDao.save(b);
        }
    }

    @Override
    public void deleteById(Long bookId) {
        //要事先判断这个BookId是不是其他表的外键，如果是，则不能删除  这个地方肯定会引起 Bug
        try {
            bookDao.deleteById(bookId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteAll(String ids) {
        //要事先判断这个BookId是不是其他表的外键，如果是，则不能删除  这个地方肯定会引起 Bug
        List<Long> Ids = new ArrayList<>();
        String[] split = ids.split(",");
        for (String s : split) {
            if (MyStringUtils.isNumeric(s)) {
                Ids.add(Long.parseLong(s));
            }
        }
        for (Long id : Ids) {
            deleteById(id);
        }
    }

    @Override
    public QueryResult<BookVO> queryByKeyWordLikeNoType(int page, int pageSize, Book param) {
        Specification<Book> specification = new Specification<Book>() {
            @Override
            public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();

                setQueryParam(root, criteriaBuilder, predicates, param);
                Predicate[] ts = new Predicate[predicates.size()];
                criteriaQuery.where(predicates.toArray(ts));
                return null;
            }
        };
        return getBookVOQueryResult(page, pageSize, specification);
    }

    @Override
    public QueryResult<BookVO> queryByKeyWordLikeWithType(int page, int pageSize, Book param, long typeId) {

        Specification<Book> specification = new Specification<Book>() {
            @Override
            public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();

                setQueryParam(root, criteriaBuilder, predicates, param);

                predicates.add(criteriaBuilder.equal(root.<Type>get("type").get("typeId"), typeId));


                Predicate[] ts = new Predicate[predicates.size()];
                criteriaQuery.where(predicates.toArray(ts));
                return null;
            }
        };
        return getBookVOQueryResult(page, pageSize, specification);
    }

    @Override
    public Page<Book> findByKeyWord(String keyword, Pageable pageable) {
        Book param = new Book();
        param.setBookWriter(keyword);
        param.setBookName(keyword);
        param.setBookDescription(keyword);
        Specification<Book> specification = new Specification<Book>() {
            @Override
            public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                setQueryParam(root, criteriaBuilder, predicates, param);
                Predicate[] ts = new Predicate[predicates.size()];
                criteriaQuery.where(predicates.toArray(ts));
                return null;
            }
        };
        //
        return bookDao.findAll(specification, pageable);
    }

    @Override
    public Page<Book> findByKeyWordWithTypeId(String keyword, Long typeId, Pageable pageable) {
        Book param = new Book();
        param.setBookWriter(keyword);
        param.setBookName(keyword);
        param.setBookDescription(keyword);

        Specification<Book> specification = new Specification<Book>() {
            @Override
            public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();

                setQueryParam(root, criteriaBuilder, predicates, param);

                predicates.add(criteriaBuilder.equal(root.<Type>get("type").get("typeId"), typeId));

                Predicate[] ts = new Predicate[predicates.size()];
                criteriaQuery.where(predicates.toArray(ts));
                return null;
            }
        };
        return bookDao.findAll(specification, pageable);
    }

    @Override
    public List<Book> findByHistory() {
        Sort sort = Sort.by(Sort.Direction.DESC, "historyRentCount");
        Pageable pageable = PageRequest.of(0, 8, sort);
        return bookDao.findAll(pageable).getContent();
    }


    private QueryResult<BookVO> getBookVOQueryResult(int page, int pageSize, Specification<Book> specification) {
        Sort sort = Sort.by(Sort.Direction.ASC, "bookId");
        Pageable pageable = PageRequest.of(page, pageSize, sort);
        Page<Book> bookPage = bookDao.findAll(specification, pageable);

        List<Book> list = bookPage.getContent();
        List<BookVO> bookVOList = new ArrayList<>();
        list.forEach(book -> bookVOList.add(BookVO.convert(book)));

        QueryResult<BookVO> queryResult = new QueryResult<>();
        queryResult.setTotalCount(bookPage.getTotalElements());
        queryResult.setData(bookVOList);
        return queryResult;
    }

    private void setQueryParam(Root<Book> root, CriteriaBuilder criteriaBuilder, List<Predicate> predicates, Book param) {
        String bookName = param.getBookName();
        Predicate q1 = criteriaBuilder.like(root.<String>get("bookName"), "%" + bookName + "%");
        String bookWriter = param.getBookWriter();
        Predicate q2 = criteriaBuilder.like(root.<String>get("bookWriter"), "%" + bookWriter + "%");
        String bookDescription = param.getBookDescription();
        Predicate q3 = criteriaBuilder.like(root.<String>get("bookDescription"), "%" + bookDescription + "%");
        predicates.add(criteriaBuilder.or(q1, q2, q3));
    }


}
